package cn.org.seventh.web.common;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.BeanUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.mvc.LastModified;
import org.springframework.web.servlet.mvc.WebContentInterceptor;
import org.springframework.web.util.WebUtils;

/**
 * 能够被代理的CustomAbstractController 行为同spring自带的AbstractController
 * 这里仅仅去掉了所有方法的final标识 使得该类方法在子类中被代理后该类方法也能被代理 从而使得该类的子类行为和原有类保持一致
 * 如果不去掉final标识会使得用代理类的引用调用非代理方法不能获取代理前的原始值 注意在被代理过得方法中的this引用的是代理前的对象
 * 未代理的final方法中的this引用是代理类对象 非final的方法中均可以this表示代理前对象 为了避免错误均采用一致行为
 * 在fianl标识的方法中凡是要保持和代理前行为一致可能需要改变this引用属性方式 解决办法为使用get set 如果原有属性没有 可能会添加访问方法
 * 
 * <p>
 * Convenient superclass for controller implementations, using the Template
 * Method design pattern.
 * </p>
 * 
 * <p>
 * As stated in the
 * {@link org.springframework.web.servlet.mvc.Controller Controller} interface,
 * a lot of functionality is already provided by certain abstract base
 * controllers. The AbstractController is one of the most important abstract
 * base controller providing basic features such as the generation of caching
 * headers and the enabling or disabling of supported methods (GET/POST).
 * </p>
 * 
 * <p>
 * <b><a name="workflow">Workflow (<a href="Controller.html#workflow">and that
 * defined by interface</a>):</b><br>
 * <ol>
 * <li>{@link #handleRequest(HttpServletRequest,HttpServletResponse) handleRequest()}
 * will be called by the DispatcherServlet</li>
 * <li>Inspection of supported methods (ServletException if request method is
 * not support)</li>
 * <li>If session is required, try to get it (ServletException if not found)</li>
 * <li>Set caching headers if needed according to cacheSeconds propery</li>
 * <li>Call abstract method
 * {@link #handleRequestInternal(HttpServletRequest,HttpServletResponse) handleRequestInternal()}
 * (optionally synchronizing around the call on the HttpSession), which should
 * be implemented by extending classes to provide actual functionality to return
 * {@link org.springframework.web.servlet.ModelAndView ModelAndView} objects.</li>
 * </ol>
 * </p>
 * 
 * <p>
 * <b><a name="config">Exposed configuration properties</a> (<a
 * href="Controller.html#config">and those defined by interface</a>):</b><br>
 * <table border="1">
 * <tr>
 * <td><b>name</b></th>
 * <td><b>default</b></td>
 * <td><b>description</b></td>
 * </tr>
 * <tr>
 * <td>supportedMethods</td>
 * <td>GET,POST</td>
 * <td>comma-separated (CSV) list of methods supported by this controller, such
 * as GET, POST and PUT</td>
 * </tr>
 * <tr>
 * <td>requireSession</td>
 * <td>false</td>
 * <td>whether a session should be required for requests to be able to be
 * handled by this controller. This ensures that derived controller can -
 * without fear of null pointers - call request.getSession() to retrieve a
 * session. If no session can be found while processing the request, a
 * ServletException will be thrown</td>
 * </tr>
 * <tr>
 * <td>cacheSeconds</td>
 * <td>-1</td>
 * <td>indicates the amount of seconds to include in the cache header for the
 * response following on this request. 0 (zero) will include headers for no
 * caching at all, -1 (the default) will not generate <i>any headers</i> and
 * any positive number will generate headers that state the amount indicated as
 * seconds to cache the content</td>
 * </tr>
 * <tr>
 * <td>synchronizeOnSession</td>
 * <td>false</td>
 * <td>whether the call to <code>handleRequestInternal</code> should be
 * synchronized around the HttpSession, to serialize invocations from the same
 * client. No effect if there is no HttpSession. </td>
 * </tr>
 * </table>
 * 
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see WebContentInterceptor
 */
public abstract class CustomAbstractController extends
		CustomWebContentGenerator implements Controller {

	private boolean synchronizeOnSession = false;

	/**
	 * Set if controller execution should be synchronized on the session, to
	 * serialize parallel invocations from the same client.
	 * <p>
	 * More specifically, the execution of the
	 * <code>handleRequestInternal</code> method will get synchronized if this
	 * flag is "true". The best available session mutex will be used for the
	 * synchronization; ideally, this will be a mutex exposed by
	 * HttpSessionMutexListener.
	 * <p>
	 * The session mutex is guaranteed to be the same object during the entire
	 * lifetime of the session, available under the key defined by the
	 * <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a safe
	 * reference to synchronize on for locking on the current session.
	 * <p>
	 * In many cases, the HttpSession reference itself is a safe mutex as well,
	 * since it will always be the same object reference for the same active
	 * logical session. However, this is not guaranteed across different servlet
	 * containers; the only 100% safe way is a session mutex.
	 * 
	 * @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal
	 * @see org.springframework.web.util.HttpSessionMutexListener
	 * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
	 */
	public void setSynchronizeOnSession(boolean synchronizeOnSession) {
		this.synchronizeOnSession = synchronizeOnSession;
	}

	/**
	 * Return whether controller execution should be synchronized on the
	 * session.
	 */
	public boolean isSynchronizeOnSession() {
		return this.synchronizeOnSession;
	}

	public final ModelAndView handleRequest(HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		// Delegate to WebContentGenerator for checking and preparing.
		checkAndPrepare(request, response, this instanceof LastModified);

		// Execute handleRequestInternal in synchronized block if required.
		if (isSynchronizeOnSession()) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return handleRequestInternal(request, response);
				}
			}
		}

		return handleRequestInternal(request, response);
	}

	/**
	 * Template method. Subclasses must implement this. The contract is the same
	 * as for <code>handleRequest</code>.
	 * 
	 * @see #handleRequest
	 */
	protected abstract ModelAndView handleRequestInternal(
			HttpServletRequest request, HttpServletResponse response)
			throws Exception;

	/**
	 * 给定视图文件名和模型
	 * 
	 * @param viewName
	 * @param model
	 * @return
	 */
	public ModelAndView getModelAndView(String viewName, Map model) {
		return new ModelAndView(viewName, model);
	}

	/**
	 * 当希望用自己定义的VIEW类型的时候 自己设定一些属性 然后再上下文中初始化
	 * 
	 * @param view
	 * @param model
	 * @return
	 */
	public ModelAndView getModelAndView(View view, Map model) {
		view = (View) getWebApplicationContext()
				.getAutowireCapableBeanFactory().initializeBean(view,
						"" + System.currentTimeMillis());
		return new ModelAndView(view, model);
	}

	/**
	 * 当希望用自己定义的VIEW类型的时候 不需要自己设定属性 自动实例并在上下文中初始化
	 * 
	 * @param view
	 * @param model
	 * @return
	 */
	public ModelAndView getModelAndView(Class viewType, Map model) {
		View view = (View) getWebApplicationContext()
				.getAutowireCapableBeanFactory().initializeBean(
						BeanUtils.instantiateClass(viewType),
						"" + System.currentTimeMillis());
		return new ModelAndView(view, model);
	}
}
